%%--------------------------------------------------------------------
%% Copyright (c) 2020-2025 EMQ Technologies Co., Ltd. All Rights Reserved.
%%--------------------------------------------------------------------
-module(emqx_mgmt_api_alarms_SUITE).

-compile(export_all).
-compile(nowarn_export_all).

-include_lib("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.hrl").

-define(ACT_ALARM, test_act_alarm).
-define(DE_ACT_ALARM, test_de_act_alarm).

all() ->
    emqx_common_test_helpers:all(?MODULE).

init_per_suite(Config) ->
    Apps = emqx_cth_suite:start(
        [
            emqx,
            emqx_management,
            emqx_mgmt_api_test_util:emqx_dashboard()
        ],
        #{work_dir => emqx_cth_suite:work_dir(Config)}
    ),
    [{suite_apps, Apps} | Config].

end_per_suite(Config) ->
    ok = emqx_cth_suite:stop(?config(suite_apps, Config)).

t_alarms_api(_) ->
    ok = emqx_alarm:activate(?ACT_ALARM),
    ok = emqx_alarm:activate(?DE_ACT_ALARM),
    ok = emqx_alarm:deactivate(?DE_ACT_ALARM),
    get_alarms(1, true),
    get_alarms(1, false).

t_alarm_cpu(_) ->
    ok.

t_delete_alarms_api(_) ->
    Path = emqx_mgmt_api_test_util:api_path(["alarms"]),
    {ok, _} = emqx_mgmt_api_test_util:request_api(delete, Path),
    get_alarms(1, true),
    get_alarms(0, false).

get_alarms(AssertCount, Activated) when is_atom(Activated) ->
    get_alarms(AssertCount, atom_to_list(Activated));
get_alarms(AssertCount, Activated) ->
    Path = emqx_mgmt_api_test_util:api_path(["alarms"]),
    Qs = "activated=" ++ Activated,
    Headers = emqx_mgmt_api_test_util:auth_header_(),
    {ok, Response} = emqx_mgmt_api_test_util:request_api(get, Path, Qs, Headers),
    Data = emqx_utils_json:decode(Response),
    Meta = maps:get(<<"meta">>, Data),
    Page = maps:get(<<"page">>, Meta),
    Limit = maps:get(<<"limit">>, Meta),
    Count = maps:get(<<"count">>, Meta),
    ?assertEqual(Page, 1),
    ?assertEqual(Limit, emqx_mgmt:default_row_limit()),
    ?assert(Count >= AssertCount).

t_force_deactivate_alarm_api(_) ->
    AlarmName = <<"test_force_deactivate_alarm">>,

    %% Test force deactivate non-existent alarm - should fail
    assert_force_deactivate_fails(
        AlarmName, <<"NOT_FOUND">>, <<"Alarm not found or already deactivated">>
    ),

    %% Activate an alarm
    ok = emqx_alarm:activate(AlarmName, #{detail => <<"test detail">>}, <<"test message">>),

    %% Verify alarm is activated
    Alarms = emqx_alarm:get_alarms(activated),
    ?assert(lists:any(fun(#{name := Name}) -> Name =:= AlarmName end, Alarms)),

    %% Test successful force deactivate
    assert_force_deactivate_success(AlarmName),

    %% Verify alarm is deactivated
    ActivatedAlarms = emqx_alarm:get_alarms(activated),
    ?assertNot(lists:any(fun(#{name := Name}) -> Name =:= AlarmName end, ActivatedAlarms)),

    %% Test force deactivate already deactivated alarm - should fail
    assert_force_deactivate_fails(
        AlarmName, <<"NOT_FOUND">>, <<"Alarm not found or already deactivated">>
    ),

    %% Test with atom alarm name
    AtomAlarmName = test_atom_alarm,
    ok = emqx_alarm:activate(AtomAlarmName, #{}, <<"atom alarm">>),
    assert_force_deactivate_success(atom_to_binary(AtomAlarmName, utf8)),

    %% Test parameter validation
    assert_force_deactivate_fails(undefined, <<"INVALID_PARAMETER">>, <<"name is required">>),
    assert_force_deactivate_fails(<<>>, <<"INVALID_PARAMETER">>, <<"name is required">>).

%% Helper function to test successful force deactivate
assert_force_deactivate_success(AlarmName) ->
    Path = emqx_mgmt_api_test_util:api_path(["alarms", "force_deactivate"]),
    Body = #{<<"name">> => AlarmName},
    {ok, _} = emqx_mgmt_api_test_util:request_api(post, Path, "", [], Body).

%% Helper function to test failed force deactivate
assert_force_deactivate_fails(AlarmName, ExpectedCode, ExpectedMessage) ->
    Path = emqx_mgmt_api_test_util:api_path(["alarms", "force_deactivate"]),
    Body =
        case AlarmName of
            undefined -> #{};
            Name -> #{<<"name">> => Name}
        end,
    Response = emqx_mgmt_api_test_util:request_api(post, Path, "", [], Body, #{return_all => true}),
    case Response of
        {error, {{_, 400, _}, _, ErrorBody}} ->
            ErrorData = emqx_utils_json:decode(ErrorBody),
            ?assertEqual(ExpectedCode, maps:get(<<"code">>, ErrorData)),
            ?assertEqual(ExpectedMessage, maps:get(<<"message">>, ErrorData));
        Other ->
            ct:fail("Expected 400 error with code ~p, but got: ~p", [ExpectedCode, Other])
    end.

t_alarm_monitor(_) ->
    AlarmName = <<"conn_congestion/test_client/test_user">>,
    TestPid = spawn(fun() ->
        AlarmDetails = #{test => details},
        AlarmMessage = <<"connection congested: test">>,
        ok = emqx_alarm:activate(AlarmName, AlarmDetails, AlarmMessage),
        %% Keep the process alive to maintain the alarm
        receive
            stop -> ok;
            _ -> ok
        end
    end),
    %% Verify alarm is activated
    timer:sleep(100),
    Alarms = emqx_alarm:get_alarms(activated),
    ?assert(lists:any(fun(#{name := Name}) -> Name =:= AlarmName end, Alarms)),

    %% Force kill the process
    exit(TestPid, kill),
    timer:sleep(100),
    ActivatedAlarms = emqx_alarm:get_alarms(activated),
    ?assertNot(lists:any(fun(#{name := Name}) -> Name =:= AlarmName end, ActivatedAlarms)),
    DeactivatedAlarms = emqx_alarm:get_alarms(deactivated),
    ?assert(lists:any(fun(#{name := Name}) -> Name =:= AlarmName end, DeactivatedAlarms)),
    ok.
